using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.IO;

using GameLibrary;
using System.Reflection;

namespace ArkanoidGame
{
    public class BricksHUD : StaticElement
    {
        #region Private Members
        private Color m_foreColour = BricksSettings.OUTLINE_COLOR;
        private Color m_backColour = BricksSettings.FILL_COLOR;
        private List<FloatText> m_floatingTextItems = new List<FloatText>();
        private List<int> m_highScores = new List<int>();
        private bool m_upToDate = false;
        #endregion

        #region Public Properties
        public Color ForeColour
        {
            get { return m_foreColour; }
            set { m_foreColour = value; }
        }

        public Color BackColour
        {
            get { return m_backColour; }
            set { m_backColour = value; }
        }

        public List<FloatText> FloatingTextItems
        {
            get { return m_floatingTextItems; }
        }

        public List<int> HighScores
        {
            get { return m_highScores; }
        }

        public bool UpToDate 
        {
            get { return m_upToDate; }
            set { m_upToDate = value;  }
        }

        public int Lives { get; set; }
        public int Level { get; set; }
        public int Score { get; set; }
        public int ScoreMultiplier { get; set; }
        public int ScoreAccumulator { get; set; }
        #endregion

        #region Constructors
        public BricksHUD(GamePanel owningGame, RectangleF rectangle)
            : base(owningGame, rectangle)
        {
        }

        public BricksHUD(GamePanel owningGame, PointF position, SizeF size)
            : this(owningGame, new RectangleF(position, size))
        {
        }
        #endregion

        #region State Draw Methods
        private void DrawGameplayHUD(TimeSpan elapsedTime, Graphics g)
        {
            ArkanoidGame owner = (ArkanoidGame)this.OwningGame;
            SolidBrush foreBrush = new SolidBrush(this.ForeColour);
            SolidBrush backBrush = new SolidBrush(this.BackColour);
            Pen forePen = new Pen(foreBrush);
            Pen backPen = new Pen(backBrush);

            g.FillRectangle(backBrush, this.Position.X, this.Position.Y, this.Size.Width, this.Size.Height);
            g.DrawRectangle(forePen, this.Position.X, this.Position.Y, this.Size.Width, this.Size.Height);

            float textY = (this.Rectangle.Top + this.Rectangle.Height / 2f) - ((float)owner.Font.Height / 2f);
            float lifeIconY = (this.Rectangle.Top + this.Rectangle.Height / 2f) - ((float)BricksSettings.BALL_HEIGHT / 2f);
            string livesStr = "Lives:";
            SizeF livesStrSize = g.MeasureString(livesStr, owner.Font);

            PointF startPoint = new PointF(
                this.Rectangle.Right - 10f - livesStrSize.Width - (BricksSettings.LIVES_MAX * (BricksSettings.BALL_WIDTH + BricksSettings.BRICK_PADDING_X)),
                (this.Rectangle.Top + this.Rectangle.Height / 2f) - (float)BricksSettings.BRICK_HEIGHT / 2f
            );

            g.DrawString(livesStr, owner.Font, foreBrush, startPoint.X, textY);
            startPoint = new PointF(startPoint.X + livesStrSize.Width + BricksSettings.BRICK_PADDING_X, startPoint.Y);

            for (int i = 0; i < BricksSettings.LIVES_MAX; i++)
            {
                if (i < this.Lives)
                {
                    g.FillEllipse(foreBrush, startPoint.X + (i * (BricksSettings.BALL_WIDTH + BricksSettings.BRICK_PADDING_X)), startPoint.Y + (float)BricksSettings.BALL_HEIGHT / 2f, BricksSettings.BALL_WIDTH, BricksSettings.BALL_HEIGHT);
                }
                else
                {
                    g.FillEllipse(backBrush, startPoint.X + (i * (BricksSettings.BALL_WIDTH + BricksSettings.BRICK_PADDING_X)), startPoint.Y + (float)BricksSettings.BALL_HEIGHT / 2f, BricksSettings.BALL_WIDTH, BricksSettings.BALL_HEIGHT);
                    g.DrawEllipse(forePen, startPoint.X + (i * (BricksSettings.BALL_WIDTH + BricksSettings.BRICK_PADDING_X)), startPoint.Y + (float)BricksSettings.BALL_HEIGHT / 2f, BricksSettings.BALL_WIDTH, BricksSettings.BALL_HEIGHT);
                }
            }

            string levelStr = string.Format("Level {0}", this.Level);
            SizeF levelStrSize = g.MeasureString(levelStr, owner.Font);
            g.DrawString(levelStr, owner.Font, foreBrush, this.Rectangle.Width / 2f - levelStrSize.Width / 2f, (int)((this.Rectangle.Top + this.Rectangle.Height / 2f) - (float)owner.Font.Height / 2f));

            string scoreStr = string.Format("Score: {0}", this.Score);
            SizeF scoreStrSize = g.MeasureString(scoreStr, owner.Font);
            g.DrawString(scoreStr, owner.Font, foreBrush, 10f, textY);
        }

        private void DrawExtraTextItems(TimeSpan elapsedTime, Graphics g)
        {
            foreach (FloatText ft in m_floatingTextItems)
            {
                ft.Draw(elapsedTime, g);
            }

        }

        private void DrawStartBox(TimeSpan elapsedTime, Graphics g)
        {
            ArkanoidGame owner = (ArkanoidGame)this.OwningGame;
            SolidBrush foreBrush = new SolidBrush(this.ForeColour);
            SolidBrush backBrush = new SolidBrush(this.BackColour);
            Pen forePen = new Pen(foreBrush);
            Pen backPen = new Pen(backBrush);

            string startText = "Left click to start...";
            SizeF startTextSize = g.MeasureString(startText, owner.Font);
            RectangleF startTextBox = new RectangleF(
                owner.InitialGameSize.Width / 2f - startTextSize.Width / 2f - 25f,
                owner.InitialGameSize.Height / 2f - startTextSize.Height / 2f - 5f,
                startTextSize.Width + 50f,
                startTextSize.Height + 10f
            );

            g.FillRectangle(backBrush, startTextBox);
            g.DrawRectangle(forePen, startTextBox.Left, startTextBox.Top, startTextBox.Width, startTextBox.Height);
            g.DrawString(startText, owner.Font, foreBrush, new PointF(startTextBox.Left + startTextBox.Width / 2f - startTextSize.Width / 2f, startTextBox.Top + startTextBox.Height / 2f - startTextSize.Height / 2f));
        }


        private void UpdateScoresFromFile() 
        {
            if (!m_upToDate)
            {
                string exePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                string scoresPath = Path.Combine(exePath, "HighScores.txt");
                FileInfo scores = new FileInfo(scoresPath);
                List<int> storedScores = new List<int>();
                using (StreamReader reader = new StreamReader(scoresPath))
                {
                    while (reader.Peek() != -1)
                    {
                        storedScores.Add(Int32.Parse(reader.ReadLine()));
                    }

                }

                foreach (int score in m_highScores)
                {
                    storedScores.Add(score);
                }

                storedScores.Sort();
                storedScores.Reverse();

                if (storedScores.Count > 10)
                {
                    for (int i = 10; i < storedScores.Count; i++)
                    {
                        storedScores.Remove(storedScores[i]);
                    }
                }


                using (StreamWriter writer = new StreamWriter(scoresPath))
                {
                    foreach (int score in storedScores)
                    {
                        writer.WriteLine(score);
                    }
                }
                m_upToDate = true;
                m_highScores = storedScores; 
            }
        }


        private void DrawHighScores(TimeSpan elasedTime, Graphics g)
       {
           ArkanoidGame owner = (ArkanoidGame)this.OwningGame;
           SolidBrush foreBrush = new SolidBrush(this.ForeColour);
           SolidBrush backBrush = new SolidBrush(this.BackColour);
           Pen forePen = new Pen(foreBrush);
           Pen backPen = new Pen(backBrush);

           UpdateScoresFromFile();
 
           if (owner.HUD.FloatingTextItems.Count == 0)
           {
               SizeF hsBoxSize = new SizeF(owner.InitialGameSize.Width * 0.75f, owner.InitialGameSize.Height * 0.75f);
               RectangleF hsBox = new RectangleF(
                   owner.InitialGameSize.Width / 2f - hsBoxSize.Width / 2f,
                   owner.InitialGameSize.Height / 2f - hsBoxSize.Height / 2f,
                   hsBoxSize.Width,
                   hsBoxSize.Height
               );
 
               g.FillRectangle(backBrush, hsBox);
               g.DrawRectangle(forePen, hsBox.Left, hsBox.Top, hsBox.Width, hsBox.Height);
               g.DrawLine(forePen, hsBox.Left, hsBox.Top + (float)owner.Font.Height * 1.25f, hsBox.Right, hsBox.Top + (float)owner.Font.Height * 1.25f);
 
               string hsText = "High Scores";
               SizeF hsTextSize = g.MeasureString(hsText, owner.Font);
 
               g.DrawString(hsText, owner.Font, foreBrush, new PointF(hsBox.Left + hsBox.Width / 2f - hsTextSize.Width / 2f, hsBox.Top + ((float)owner.Font.Height * 1.25f) / 2f - hsTextSize.Height / 2f));
 
               PointF scoreTextPos = new PointF(hsBox.Left + BricksSettings.HIGHSCORE_TEXTPADDING_X, hsBox.Top + ((float)owner.Font.Height * 1.25f) + BricksSettings.HIGHSCORE_TEXTPADDING_Y);
               bool didHighlight = false;
               for (int i = 0; i < 10; i++)
               {
                   string scoreText = string.Format("{0, 2}. {1}", i + 1, (i < m_highScores.Count) ? m_highScores[i] : 0);
                   g.DrawString(
                       scoreText,
                       owner.Font,
                       (!didHighlight && i < m_highScores.Count && this.Score == m_highScores[i]) ? Brushes.White : foreBrush,
                       new PointF(
                           scoreTextPos.X,
                           scoreTextPos.Y + ((float)i * hsTextSize.Height)
                       )
                   );

                   if (!didHighlight && i < m_highScores.Count && this.Score == m_highScores[i])
                   { 
                       didHighlight = true; 
                   }
               }
 
               string nextText = "Left click to start a new game!";
               SizeF textSize = g.MeasureString(nextText, owner.Font);
               g.DrawString(
                   nextText,
                   owner.Font,
                   foreBrush,
                   new PointF(hsBox.Left + hsBox.Width / 2f - textSize.Width / 2f, hsBox.Bottom - textSize.Height - BricksSettings.HIGHSCORE_TEXTPADDING_Y)
               );
           }
       }
        #endregion

        #region Public Methods
        public void UpdateHighScores()
        {
            m_highScores.Add(this.Score);
            m_highScores.Sort(
                (lhs, rhs) =>
                {
                    if (lhs < rhs)
                    {
                        return 1;
                    }
                    else
                    {
                        if (lhs > rhs)
                        {
                            return -1;
                        }
                        else
                        {
                            return 0;
                        }
                    }
                }
            );

            if (m_highScores.Count > BricksSettings.SCORE_MAXHIGHSCORES)
            {
                m_highScores.RemoveRange(BricksSettings.SCORE_MAXHIGHSCORES, m_highScores.Count - BricksSettings.SCORE_MAXHIGHSCORES);
            }
        }
        #endregion

        #region IGameElement Implementation
        public override void Draw(TimeSpan elapsedTime, Graphics g)
        {
            if (g != null)
            {
                ArkanoidGame owner = (ArkanoidGame)this.OwningGame;

                switch (owner.State)
                {
                    case GameState.Ready:
                        DrawGameplayHUD(elapsedTime, g);
                        DrawStartBox(elapsedTime, g);
                        DrawExtraTextItems(elapsedTime, g);
                        break;
                    case GameState.Playing:
                    case GameState.LevelComplete:
                        DrawGameplayHUD(elapsedTime, g);
                        DrawExtraTextItems(elapsedTime, g);
                        break;
                    case GameState.GameOverFailed:
                    case GameState.GameOverSuccess:
                        DrawHighScores(elapsedTime, g);
                        DrawExtraTextItems(elapsedTime, g);
                        break;
                }
            }
        }

        public override void Update(TimeSpan elapsedTime)
        {
            for (int i = 0; i < m_floatingTextItems.Count; i++)
            {
                m_floatingTextItems[i].Update(elapsedTime);

                if (m_floatingTextItems[i].Alive == false)
                {
                    m_floatingTextItems.RemoveAt(i);
                    i--;
                }
            }
        }
        #endregion
    }
}